package interpreter

import (
	"errors"
	"fmt"
	"learning/gooop/behavioral_patterns/interpreter/tokens"
	"strconv"
)

type tSQLParser struct {
	result *ParseResult
}


func newSQLParser() ISQLParser {
	return &tSQLParser{
		newParseResult(),
	}
}

func (me *tSQLParser) Parse(sql string) (error,*ParseResult) {
	lexer := newLexer(sql)
	e, tks := lexer.Parse()
	if e != nil {
		return e, nil
	}

	queue := newTokenQueue(tks)

	// select (* | field-list)
	e = me.ParseSelectPart(queue)
	if e != nil {
		return e, nil
	}

	// from
	e = me.ParseFromPart(queue)
	if e != nil {
		return e, nil
	}

	// where + bool_expression
	e = me.ParseWherePart(queue)
	if e != nil {
		return e, nil
	}

	// eof
	if !queue.IsEmpty() {
		_,t := queue.Poll()
		return errors.New(fmt.Sprintf("expecting EOF at %v", t.from)), nil
	}

	return nil, me.result
}

func (me *tSQLParser) ParseSelectPart(queue *tTokenQueue) error {
	b,v := queue.Poll()
	if !b {
		return errors.New("expecting SELECT keyword")
	}

	if v.token != tokens.Select {
		return errors.New(fmt.Sprintf("expecting SELECT keyword but found '%s'", v.value))
	}

	fields := make([]string, 0)

	b,v = queue.Peek()
	if !b {
		return errors.New("unexpected EOF")
	}

	switch v.token {
	case tokens.Star:
		queue.Poll()
		fields = append(fields, v.value)
		break

	case tokens.Identifier:
		queue.Poll()
		fields = append(fields, v.value)
		break

	default:
		return errors.New(fmt.Sprintf("expecting column name but found '%s'", v.value))
	}

	for {
		b,v := queue.Peek()
		if !b {
			break
		}

		if v.token != tokens.Comma {
			break
		}
		queue.Poll()

		b,v = queue.Peek()
		if !b || v.token != tokens.Identifier {
			return errors.New(fmt.Sprintf("expecting column name but found '%s'", v.value))
		}
		queue.Poll()
		fields = append(fields, v.value)
	}

	if len(fields) > 0 {
		me.result.Fields = fields
		return nil
	}

	return errors.New("expecting column names")
}


func (me *tSQLParser) ParseFromPart(queue *tTokenQueue) error {
	b,v1 := queue.Poll()
	if !b {
		return errors.New("expecting 'from', but eof")
	}

	if v1.token != tokens.From {
		return errors.New(fmt.Sprintf("expecting 'from' at %v", v1.from))
	}

	b,v2 := queue.Poll()
	if !b {
		return errors.New("expecting table name, but eof")
	}
	if v2.token == tokens.Identifier {
		me.result.Table = v2.value
		return nil
	}

	return errors.New(fmt.Sprintf("expecting table name at %v", v2.from))
}


func (me *tSQLParser) ParseWherePart(queue *tTokenQueue) error {
	if queue.IsEmpty() {
		// no where clause
		me.result.RowFilter = newEmptyRowFilter(true)
		return nil
	}

	_,v1 := queue.Poll()
	if v1.token != tokens.Where {
		return errors.New(fmt.Sprintf("expecting 'where' keyword at %v", v1.from))
	}

	stack := newArrayStack()
	e, expression := me.ParseWhereExpression(queue, stack)
	if e != nil {
		return e
	}

	me.result.RowFilter = newExpressionRowFilter(expression)
	return nil
}


func (me *tSQLParser) ParseWhereExpression(queue *tTokenQueue, stack *tArrayStack) (error, IBoolExpression) {
	for {
		if queue.IsEmpty() {
			break
		}

		_,t := queue.Poll()
		switch t.token {
		case tokens.LB:
			stack.Push(t)
			break

		case tokens.Identifier:
			stack.Push(t)
			break

		case tokens.Equal:
			stack.Push(t)
			break

		case tokens.NotEqual:
			stack.Push(t)
			break

		case tokens.Like:
			stack.Push(t)
			break

		case tokens.NotLike:
			stack.Push(t)
			break

		case tokens.Greater:
			stack.Push(t)
			break

		case tokens.GreaterEqual:
			stack.Push(t)
			break

		case tokens.Less:
			stack.Push(t)
			break

		case tokens.LessEqual:
			stack.Push(t)
			break

		case tokens.And:
			stack.Push(t)
			break

		case tokens.OR:
			stack.Push(t)
			break

		case tokens.StringLiteral:
			// field op string
			b, v := stack.Pop()
			if !b || !v.IsToken() {
				return errors.New(fmt.Sprintf("expecting operator before %s", t.value)), nil
			}

			op := v.(*tTokenNode)
			if !me.TokenIn(op.token, tokens.Equal, tokens.NotEqual, tokens.Like, tokens.NotLike) {
				return errors.New(fmt.Sprintf("expecting string operator before %s", t.value)), nil
			}

			b,v = stack.Pop()
			if !b || !v.IsToken() {
				return errors.New(fmt.Sprintf("expecting column name before %s", op.from)), nil
			}
			field := v.(*tTokenNode)
			if field.token != tokens.Identifier {
				return errors.New(fmt.Sprintf("expecting column name at %v", field.from)), nil
			}

			exp := newStringFieldExpression(op.token, field.value, t.value)
			e := me.PushExpression(exp, stack)
			if e != nil {
				return e, nil
			}
			break


		case tokens.IntLiteral:
			// field op int
			b, v := stack.Pop()
			if !b || !v.IsToken() {
				return errors.New(fmt.Sprintf("expecting operator before %s", t.value)), nil
			}

			op := v.(*tTokenNode)
			if !me.TokenIn(op.token, tokens.Equal, tokens.NotEqual, tokens.Greater, tokens.GreaterEqual, tokens.Less, tokens.LessEqual) {
				return errors.New(fmt.Sprintf("expecting int operator before %s", t.value)), nil
			}

			b,v = stack.Pop()
			if !b || !v.IsToken() {
				return errors.New(fmt.Sprintf("expecting column name before %v", op.from)), nil
			}
			field := v.(*tTokenNode)
			if field.token != tokens.Identifier {
				return errors.New(fmt.Sprintf("expecting column name at %v", field.from)), nil
			}

			i,_ := strconv.Atoi(t.value)
			exp := newIntFieldExpression(op.token, field.value, i)
			e := me.PushExpression(exp, stack)
			if e != nil {
				return e, nil
			}
			break


		case tokens.RB:
			// )
			b,v := stack.Pop()
			if !b || !v.IsExpression() {
				return errors.New(fmt.Sprintf("expecting expression before %v", t.from)), nil
			}
			expression := v.(*tExpressionNode).Expression

			b,v = stack.Pop()
			if !b || !v.IsToken() {
				return errors.New(fmt.Sprintf("expected ( not found at %v", t.from)), nil
			}
			lb := v.(*tTokenNode)
			if lb.token != tokens.LB {
				return errors.New(fmt.Sprintf("expected ( not found at %v", t.from)), nil
			}

			e := me.PushExpression(expression, stack)
			if e != nil {
				return e, nil
			}
			break
		}
	}


	if stack.Size() != 1{
		return errors.New("invalid expression"), nil
	}
	ok, node := stack.Peek()
	if !ok || !node.IsExpression() {
		return errors.New("invalid expression"), nil
	}

	return nil,node.(*tExpressionNode).Expression
}


func (me *tSQLParser) TokenIn(t tokens.Tokens, args... tokens.Tokens) bool {
	for _,it := range args {
		if it == t {
			return true
		}
	}
	return false
}


func (me *tSQLParser) PushExpression(exp IBoolExpression, stack *tArrayStack) error {
	b,n := stack.Peek()
	if !b {
		stack.Push(newExpressionNode(exp))
		return nil
	}

	if !n.IsToken() {
		return errors.New("expecting and/or/(")
	}

	t := n.(*tTokenNode)
	if !me.TokenIn(t.token, tokens.And, tokens.OR, tokens.LB) {
		return errors.New("expecting and/or/(")
	}

	if me.TokenIn(t.token, tokens.And, tokens.OR) {
		stack.Pop()
		b,n = stack.Pop()
		if !b || !n.IsExpression(){
			return errors.New("expecting bool expression")
		}
		e := n.(*tExpressionNode)
		return me.PushExpression(newLogicNode(t.token, e.Expression, exp), stack)

	} else {
		stack.Push(newExpressionNode(exp))
		return nil
	}
}